package mainPNN;

import java.util.ArrayList;
import java.util.Iterator;

import util.PadraoTreinamento;
import util.Util;

public class PNN2 {
	private ArrayList<PadraoTreinamento> aPadraoTreinamento = null;
	
	public PNN2(ArrayList<PadraoTreinamento> pPadraoTreinamento) {
		super();
		this.aPadraoTreinamento = pPadraoTreinamento;
	}
	
	public ArrayList<PadraoTreinamento> executar() {

		ArrayList<PadraoTreinamento> A = new ArrayList<PadraoTreinamento>();
		ArrayList<PadraoTreinamento> B = aPadraoTreinamento;
		int merge = 0;
		
		//Valores iniciais
		Double w[] = new Double[B.size()]; //Menor distancia entre ti e o prototipo mais proximo da MESMA classe.
		Double b[] = new Double[B.size()]; //Menor distancia entre ti e o prototipo mais proximo de classe DIFERENTE.
		
		for (int i = 0; i < w.length; i++) {
			w[i] = 0.0d;
			b[i] = 1000000.0d;
		}

		
		//1) Start with an arbitrary point tj in B* and assign it to A*.
		PadraoTreinamento tj = Util.getPadraoAleatorio(B, true);
		A.add(tj);
	
		merge = 0;	
		
		//2) For all points tk in B* such that class(tk) != class(tj), update bk to be the distance d(tk, tj) between tk and tj) if this distance is smaller than the present bk. Otherwise, bk in unchanged.
		for(int i = 0; i < B.size();  i++) {
			PadraoTreinamento tk = B.get(i);
			if (tk.getClasse() != tj.getClasse()) {
				Double distanciaOutraClasse = Util.getDistanciaEuclidiana(tk, tj);
				if (distanciaOutraClasse < b[i]) {
					b[i] = Util.getDistanciaEuclidiana(tk, tj);
				}
			}
		}
		
		//Find a point p in A and a point q in B such that the distance is the shortest among all distances between points of A and B.
		for (int i = 0; i < B.size(); i++) {
			
			for(int j = 0; j < B.size();  j++) {
				PadraoTreinamento tk = B.get(j);
				if (tk.getClasse() != tj.getClasse()) {
					Double distanciaOutraClasse = Util.getDistanciaEuclidiana(tk, tj);
					if (distanciaOutraClasse < b[j]) {
						b[j] = Util.getDistanciaEuclidiana(tk, tj);
					}
				}
			}
			
			int indiceMenorDistanciaOutraClasse = Util.getMenorValor(b);
			PadraoTreinamento ts = B.get(indiceMenorDistanciaOutraClasse);
			PadraoTreinamento padraoA = Util.getPadraoMaisProximo(ts, A);
			
			if (ts.getClasse() == padraoA.getClasse()) {
				PadraoTreinamento padraoMerge = this.merge(ts, padraoA);
			} else {
				A.add(ts);
				B.remove(ts);
			}
			
		}
	
		
		//3) Among all points in B*, find a point ts which has the smallest bs associated with it.
		int indiceMenorDistanciaOutraClasse = Util.getMenorValor(b);
		PadraoTreinamento ts = B.get(indiceMenorDistanciaOutraClasse);
		
		//4) If tj is not the nearest point to ts such that the classes of tj and ts are different, go to 6). Otherwise, continue.
		
		
		//5) Check whether or not d(tj, ts) is less than bj. If no, go to 6). If yes, let bj = d(tj, ts) and continue.
		//6) Let j = s, move ts from B* to A*, and go to 2) until B* is empty. When B* is empty, the final b1, b2, ..., bm are the desired ones.
		//
		//
		
		
		
		
		return aPadraoTreinamento;
		
		
	}

	private PadraoTreinamento merge(PadraoTreinamento padraoA, PadraoTreinamento padraoB) {
		ArrayList<Double> valoresA = padraoA.getValoresPadrao();
		ArrayList<Double> valoresB = padraoB.getValoresPadrao();
		ArrayList<Double> tempMerge = new ArrayList<Double>();
		
		int i = 0;
		
		while(i < valoresA.size()) {
			tempMerge.add(((valoresA.get(i) * padraoA.getPeso()) + (valoresB.get(i) * padraoB.getPeso())) / (padraoA.getPeso() + padraoB.getPeso()));
			i++;
		}
		
		PadraoTreinamento padraoRetorno = new PadraoTreinamento();
		padraoRetorno.setValoresPadrao(tempMerge);
		padraoRetorno.setClasse(padraoA.getClasse());
		padraoRetorno.setPeso(padraoA.getPeso() + padraoB.getPeso());
		
		return padraoRetorno;
	}

	private PadraoTreinamento[] obterPadroesMaisProximos(ArrayList<PadraoTreinamento> pArrayT, ArrayList<PadraoTreinamento> pArrayTPNN) {
		PadraoTreinamento[] arrayRetorno = new PadraoTreinamento[2];
		Double distEuclidiana = null;
		double menorDistancia = 1000000;
		
		PadraoTreinamento padraoTPNN = null;
		PadraoTreinamento padraoT = null;
		
		//Acha os padroes mais proximos.
		for (Iterator<PadraoTreinamento> iterator = pArrayTPNN.iterator(); iterator.hasNext();) {
			padraoTPNN = iterator.next();
			for (Iterator<PadraoTreinamento> itArrayT = pArrayT.iterator(); itArrayT.hasNext();) {
				padraoT = itArrayT.next();
				
				distEuclidiana = Util.getDistanciaEuclidiana(padraoT, padraoTPNN);
				if(distEuclidiana.doubleValue() < menorDistancia) {	
					arrayRetorno[0] = padraoT;
					arrayRetorno[1] = padraoTPNN;
					menorDistancia = distEuclidiana.doubleValue();
				}		
			}
			
		}
		
		if (arrayRetorno[0] == null) {
			System.out.println("Retorno null!");
		}
		return arrayRetorno;
	}
}
